const token = require('./token')
const {calibrate} = require('../async/')
const {getResult, getError} = require('../handle')
/**
 * 处理客户端信息。是在客户端的uni.getSystemInfo的基础之上，增加了一些额外的信息
 * 
 * clientIP：客户端ip
 * userAgent：客户端ua，注意非本地运行环境下客户端getSystemInfoSync也会获取ua参数并上传给云对象，但是云对象会从http请求头里面获取ua而不是clientInfo里面的ua
 * source：调用来源，返回值见下
 * requestId：请求id
 * scene：场景值。客户端uni.getLaunchOptionsSync返回的scene参数
 * 
 * source类型：
 * 	client：uni-app客户端导入云对象调用
 *  function：由其他云函数或云对象调用
 *  http：云对象URL化后通过http访问调用 HBuilderX 3.5.2+
 *  timing：定时任务调用云对象 HBuilderX 3.5.2+
 *  server：云函数上传并运行
 */
const handleClientInfo = (clientInfo) => {
	const {
		appId = '__UNI__D7C6E90',
		appLanguage = 'zh-Hans',
		appName = 'Fengherili',
		appVersion = '1.0.0',
		appVersionCode = '100',
		browserName = 'safari',
		browserVersion = '13.0.3',
		clientIP = '127.0.0.1',
		deviceId = '16844048099189528980',
		deviceModel = 'iPhone',
		deviceOrientation = 'portrait',
		devicePixelRatio = 2,
		deviceType = 'phone',
		hostLanguage = 'zh-CN',
		hostName = 'safari',
		hostTheme = 'light',
		hostVersion = '13.0.3',
		locale = 'zh-Hans',
		os = 'ios',
		osName = 'ios',
		osVersion = '13.2.3',
		platform = 'web',
		requestId = 'a4808368-41ee-4106-9ec0-b4a82b069b62',
		safeArea = {left: 0, right: 414, top: 0, bottom: 896, width: 414, height: 896},
		safeAreaInsets = {top: 0, right: 0, bottom: 0, left: 0},
		scene = 1001,
		screenHeight = 896,
		screenWidth = 414,
		secretType = 'none',
		source = 'client',
		statusBarHeight = 0,
		ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
		uniCompileVersion = '3.7.11',
		uniPlatform = 'web',
		uniRuntimeVersion = '3.7.11',
		userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
		windowBottom = 0,
		windowHeight = 852,
		windowTop = 44,
		windowWidth = 414,
		// 以下是云函数中得到的内容
		// "PLATFORM": "web",
		// "OS": "ios",
		// "APPID": "__UNI__5A59F0A",
		// "DEVICEID": "17368146367586182830",
		// "scene": 1001,
		// "appId": "__UNI__5A59F0A",
		// "appLanguage": "zh-Hans",
		// "appName": "产品",
		// "appVersion": "1.0.0",
		// "appVersionCode": "100",
		// "browserName": "safari",
		// "browserVersion": "16.6",
		// "deviceId": "17368146367586182830",
		// "deviceModel": "iPhone",
		// "deviceType": "phone",
		// "hostName": "safari",
		// "hostVersion": "16.6",
		// "osName": "ios",
		// "osVersion": "16.6",
		// "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
		// "uniCompilerVersion": "4.45",
		// "uniPlatform": "web",
		// "uniRuntimeVersion": "4.45",
		// "locale": "zh-Hans",
		// "LOCALE": "zh-Hans",
		// "secretType": "none",
		// "SOURCE": "client",
		// "CLIENTIP": "127.0.0.1",
		// "CLIENTUA": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
		// "SPACEINFO": {
		//     "provider": "aliyun",
		//     "spaceId": "mp-09fca185-7ab5-4c5a-9679-0a0f411c016d",
		//     "useOldSpaceId": false
		// },
		// "FUNCTION_NAME": "b-language",
		// "requestId": "7c49fe16-5be4-4c56-994e-323b54a43f45",
		// "RUNTIME_ENV": "local",
		// "FUNCTION_TYPE": "cloudfunction"
	} = clientInfo
}

/**
 * 统一请求前处理
 * request：当前的请求信息
 * 	demand的内容要求：{ // 以下具体可以不存在，不存在表示不验证该项
		is = true, // 是否要验证token。不存在就不通过
		apply, // 验证应用类型。如果存在就验证
		partya, // 验证甲方。唯一性验证
		target, // 验证项目。唯一性验证
		rank, // 验证用户级别。排除前两位字母，比较后面的数字，越小级别越大
		authority, // 验证用户权限。只要用户权限与要求权限有重叠的就满足
		user, // 验证用户。限定用户访问的云对象
		callback, // 回调验证。code和message。code === 0表示通过
		httpBlock, // 外网访问的回调，要写入数据库
		isFirst, // 是否是第一次从前端进来。因为在服务器之间会有跳转，有时候需要记录前端第一次进来的情况
 }
 */
module.exports = (request, demand, common, basedata, base64, httpBlock, isFirst) => {
	// 根据原始数据初始化
	Object.keys(common.SourceData).forEach(key => {
		if (typeof(common.SourceData[key] === 'object')) {
			if (Array.isArray(common.SourceData[key])) {
				common[key] = [...common.SourceData[key]]
			} else {
				common[key] = {...common.SourceData[key]}
			}
		} else {
			common[key] = common.SourceData[key]
		}
	})
	const {
		event, context, // 云函数只有这两个
		getUniCloudRequestId, getCloudInfo, getMethodName, getUniIdToken, getHttpInfo, getClientInfo, // 云对象的特点。必须使用点语法才能使用
	} = request
	// 云函数的类型
	const {SOURCE, requestId, CLIENTIP} = context || {}
	switch (SOURCE){
		case 'client': // 客户端callFunction方式调用
			break
		case 'http': // 云函数url化方式调用。要处理queryStringParameters和header
			break
		case 'timing': // 定时触发器调用
			break
		case 'function': // 由其他云函数callFunction调用
			break
		case 'server': // 由uniCloud管理端调用，HBuilderX里上传并运行
			break
		default:
			break;
	}
	// 获取当前请求id。出错时记录日志。log(requestId, error)
	common.requestId = request.getUniCloudRequestId ? request.getUniCloudRequestId() : (requestId || '未知')
	// 获取云端信息
	const cloudInfo = request.getCloudInfo ? request.getCloudInfo() : (context || {})
	// 回调处理云端信息，将会记录到common中
	common.cloudInfo = cloudInfo
	const spaceId = cloudInfo.spaceId || (cloudInfo.SPACEINFO && cloudInfo.SPACEINFO.spaceId)
	spaceId && Object.keys(basedata).forEach(key => {
		if (basedata[key].spaceId === spaceId) {
			// 确定当前云服务空间
			basedata.current = key
			common.spaceName = key
		}
	})
	// 记录当前云函数云对象名称
	common.functionName = cloudInfo.functionName || cloudInfo.FUNCTION_NAME
	// 获取当前调用的方法名
	common.methodName = request.getMethodName ? request.getMethodName() : undefined
	// 获取客户端token。云对象自动管理uni-id的token。开发者无需手动管理。如果不了解uni-id，请参考https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#
	// const token = request.getUniIdToken ? request.getUniIdToken() : (event && event.uniIdToken)
	// 获取开始时间
	common.startTime = Date.now()
	// 获取url化时的http信息。仅可在云对象url化时使用
	const httpInfo = request.getHttpInfo ? request.getHttpInfo() : SOURCE === 'http' ? JSON.parse(JSON.stringify(event)) : undefined
	
	/**
	 * 验证通过
	 * 
	 * 如果验证不通过，执行fail('不通过的原因')即可
	 */
	const verifySuccess = (data) =>  {
		console.log('验证通过')
	}
	
	/**
	 * 验证处理 Ca-Token
	 * 以及其他CU-开头的自定义参数
	 */
	const handleAction = (headers) => {
		if (headers) {
			// 参数校验，根据在api中注册的参数信息判断是否满足条件。如果校验不通过，直接抛出错误
			const res = calibrate(common.functionName, common.methodName, httpInfo ? httpInfo.query : common.params.length > 0 ? common.params[0] : undefined, false)
			if (res) {
				throw getError(res)
			}
			// 系统Token
			var SystemToken
			const header = {}, necessary = {}
			Object.keys(headers).forEach(key => {
				if (key === 'nouserdb') {
					// 目前仅以布尔值来判定是否需要用户信息来操作
					common.nouserdb = !!headers[key]
				} else if (key === 'initial') {
					if (typeof(headers.initial) === 'string') {
						// 只有从云对象过来的才有该字段，用以记录用户最初的信息
						const initial = JSON.parse(headers.initial)
						if (initial && typeof(initial) === 'object') {
							headers.initial = initial
							// 以记录的最初的用户信息为准
							common.initial = initial
						} else {
							delete headers.initial
						}
					}
				} else {
					// 如果仅以Ca-开头的，表示该系统的特定参数；如果以Cu-开头的，表示自定义的参数。实际url中又全部变为小写
					if (key.length > 3 && key.substr(2, 1) === '-') {
						const value = headers[key]
						key = key.toLowerCase()
						if (key === 'ca-token') {
							SystemToken = value
						} else {
							var newKey = key.substr(3), oldKey = key.substr(0, 3)
							newKey = newKey.substr(0, 1).toUpperCase() + newKey.substr(1)
							if (oldKey === 'cu-') {
								header[newKey] = value
							} else if (oldKey === 'ca-') {
								necessary[newKey] = value
							}
						}
					}
				}
			})
			common.header = header
			Object.keys(necessary).forEach(key => {
				common.necessary[key] = necessary[key]
			})
			/**
			 * 系统验证
			 */
			if (demand === true) {
				// 表示需要验证token。但是没有具体的要求。只验证是否合法是否过期
				demand = {
					is: true
				}
			}
			const demandParams = {
				current: common.startTime,
				...demand
			}
			if (!demand) {
				// 不做任何验证，直接带解析结果返回
				demandParams.onlyAnalyse = true
			}
			token.verify(SystemToken, demandParams, res => {
				if (res) {
					Object.keys(common.necessary).forEach(key => {
						if (res[key]) {
							common.necessary[key] = res[key]
						}
					})
				}
				// 验证通过
				verifySuccess()
			}, (error) => {
				// 系统登录验证失败
				throw getError(error.message, error.code + 700)
			})
		} else {
			// 没有请求头或者necessary都将报参数有误！
			throw getError('参数有误')
		}
		// 不管有没有httpInfo，都要赋值
		common.httpInfo = httpInfo
	}
	
	if (httpInfo) { // url化才有。将所得结果写入a-a-url数据库
		var {
			path = '', // HTTP请求路径，如 /hello
			httpMethod = '', // HTTP请求方法，如 GET
			headers = {}, // HTTP请求头
			queryStringParameters = {}, // HTTP请求的Query，键值对形式。‘GET’时的参数
			body = '', // HTTP请求体。‘POST’时的参数
			isBase64Encoded = false // true or false，表示body是否为Base64编码
		} = httpInfo
		common.ip = CLIENTIP || request.CLIENTIP
		handleAction(headers)
		// 进一步解析。必要的话写入数据库以记录
		if (isBase64Encoded && body) {
			// 如果isBase64Encoded，在记录时先解码。如果要测试，不要解码
			body = base64.decode(body)
		}
		const bodyObj = body ? JSON.parse(body) : {}
		Object.keys(queryStringParameters).forEach(key => {
			var value = queryStringParameters[key]
			if (typeof(value) === 'string' && value.substr(0, 1) === '{' && value.substr(-1) === '}') {
				bodyObj[key] = JSON.parse(value)
			} else {
				bodyObj[key] = value
			}
		})
		httpInfo.query = bodyObj
		if (event) { // 重新给event赋值，只赋值参数httpInfo.query
			Object.keys(event).forEach(key => {
				delete event[key]
			})
			Object.keys(bodyObj).forEach(key => {
				event[key] = bodyObj[key]
			})
		}
		httpInfo.ip = common.ip
		httpInfo.space = basedata.current
		common.spaceName = basedata.current
		httpInfo.function = common.functionName
		httpInfo.domain = basedata[basedata.current].api
		httpInfo.method = httpMethod ? httpMethod.toUpperCase() : 'GET'
		delete httpInfo.httpMethod
		if (path) {
			// 只记录本云服务空间的url请求
			httpInfo.requestId = common.requestId
			if (basedata[basedata.current].urlDb) {
				const db = uniCloud.database()
				httpInfo.target = common.necessary.Target
				httpInfo.creator = common.necessary.User
				httpInfo.timestamp = common.startTime
				const date = new Date(common.startTime)
				httpInfo.timestring = date.toLocaleString()
				db.collection(basedata[basedata.current].urlDb).add(httpInfo).then(res => {
					console.log(res.result.id)
				}).catch(err => {
					console.log(err)
				})
			} else {
				// 不记录
			}
		} else {
			// 不记录
		}
	} else { // 非url化时，最后一个参数是验证参数
		// 获取客户端信息。同前端的uni.getSystemInfo
		const clientInfo = request.getClientInfo ? request.getClientInfo() : (context || {})
		common.clientInfo = clientInfo
		handleClientInfo(clientInfo)
		common.ip = CLIENTIP || clientInfo.clientIP
		// 获取当前参数列表
		const params = request.getParams ? request.getParams() : [event || {}]
		common.params = JSON.parse(JSON.stringify(params))
		const necessary = SOURCE === 'http' ? event.necessary : params.length > 0 ? params[params.length - 1] : undefined
		if (common.functionName && necessary && necessary.necessary) {
			handleAction(necessary.necessary)
		} else {
			handleAction(necessary)
		}
	}
}